package org.afs.gescol.gwt.client.presenter;

import java.util.ArrayList;
import java.util.List;

import org.afs.gescol.gwt.client.event.EditPersonneEvent;
import org.afs.gescol.gwt.client.event.EditPersonneEventHandler;
import org.afs.gescol.gwt.client.factory.AppClientFactory;
import org.afs.gescol.gwt.client.i18n.AppMessages;
import org.afs.gescol.gwt.client.service.EleveServicesGwt;
import org.afs.gescol.gwt.client.service.EleveServicesGwtAsync;
import org.afs.gescol.gwt.client.service.PersonnelServicesGwt;
import org.afs.gescol.gwt.client.service.PersonnelServicesGwtAsync;
import org.afs.gescol.gwt.client.util.ActionsKeysUtil;
import org.afs.gescol.gwt.shared.CellPersonneModel;
import org.afs.gescol.gwt.shared.EleveModel;
import org.afs.gescol.gwt.shared.PersonnelModel;
import org.afs.gescol.gwt.shared.SearchCriteriaModel;
import org.scub.foundation.framework.gwt.module.client.callback.ConfirmCallback;
import org.scub.foundation.framework.gwt.module.client.mvp.management.confirm.ConfirmEvent;
import org.scub.foundation.framework.gwt.module.client.mvp.management.error.ErrorEvent;
import org.scub.foundation.framework.gwt.module.client.mvp.management.loading.LoadingEvent;
import org.scub.foundation.framework.gwt.module.client.mvp.management.loading.LoadingEvent.LoadingType;
import org.scub.foundation.framework.gwt.module.client.mvp.presenter.Presenter;
import org.scub.foundation.framework.gwt.module.client.mvp.view.View;
import org.scub.foundation.framework.gwt.module.client.util.composants.pagination.event.PageChangeEvent;
import org.scub.foundation.framework.gwt.module.client.util.composants.pagination.event.PageChangeHandler;
import org.scub.foundation.framework.gwt.module.client.util.composants.remote.paging.event.ColumnSortEvent;
import org.scub.foundation.framework.gwt.module.client.util.composants.remote.paging.event.ColumnSortHandler;
import org.scub.foundation.framework.gwt.module.client.util.composants.remote.paging.event.HasRemotePagingTableHandlers;
import org.scub.foundation.framework.gwt.module.client.util.composants.suggestbox.SuggestListBoxSingle;
import org.scub.foundation.framework.gwt.module.client.util.composants.suggestbox.SuggestListBoxSuggestEvent;
import org.scub.foundation.framework.gwt.module.client.util.composants.suggestbox.SuggestListBoxSuggestEventHandler;
import org.scub.foundation.framework.gwt.module.shared.IdLabelModel;
import org.scub.foundation.framework.gwt.module.shared.pagination.RemotePagingCriteriasModel;
import org.scub.foundation.framework.gwt.module.shared.pagination.RemotePagingResultsModel;

import com.google.gwt.cell.client.FieldUpdater;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.HasClickHandlers;
import com.google.gwt.event.dom.client.HasKeyUpHandlers;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.KeyUpHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.user.client.History;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.HasValue;
import com.google.gwt.user.client.ui.HasWidgets;

/**
 * Presenter for the Home page.
 * @autor Kobla GBENYO (kobla.gbenyo@gmail.com)
 */
public class HomePresenter extends Presenter {

    private EleveServicesGwtAsync eleveServices = EleveServicesGwt.App.getInstance();

    private PersonnelServicesGwtAsync personnelServices = PersonnelServicesGwt.App.getInstance();

    private AppMessages messages = AppClientFactory.getMessages();

    private ActionsKeysUtil actionsUtils = AppClientFactory.getActionsKeysUtil();

    private HomeView view = AppClientFactory.getHomeView();

    public static final Long ID_TYPE_ELEVE = 1l;

    public static final Long ID_TYPE_PERSONNEL = 2l;

    private void fillTypeSearchModel(SuggestListBoxSuggestEvent<IdLabelModel> event) {
        final List<IdLabelModel> listType = new ArrayList<IdLabelModel>();
        listType.add(new IdLabelModel(ID_TYPE_ELEVE, messages.typeEleve()));
        listType.add(new IdLabelModel(ID_TYPE_PERSONNEL, messages.typePersonnel()));
        if (event.getSuggest() != null && !"".equals(event.getSuggest())) {
            listType.clear();
            if (messages.typeEleve().toLowerCase().contains(event.getSuggest().toLowerCase())) {
                listType.add(new IdLabelModel(ID_TYPE_ELEVE, messages.typeEleve()));
            } else if (messages.typePersonnel().toLowerCase().contains(event.getSuggest().toLowerCase())) {
                listType.add(new IdLabelModel(ID_TYPE_PERSONNEL, messages.typePersonnel()));
            }
        }
        event.getCallBack().onSuccess(listType);
    }

    private boolean isTypePersonnel() {
        final boolean val = view.getTypeSearchModelSuggestHandler().getValue() != null
                &&  view.getTypeSearchModelSuggestHandler().getValue().getId() != null
                && view.getTypeSearchModelSuggestHandler().getValue().getId().equals(ID_TYPE_PERSONNEL);
        if (!val) {
            view.getTypeSearchModelSuggestHandler().setValue(new IdLabelModel(ID_TYPE_ELEVE, messages.typeEleve()));
        }
        return val;
    }

    /**
     * Load the person (personnel, élèves).
     */
    public void loadPersonnes() {
        loadPersonnes(0, 0);
    }

    /**
     * Load the person.
     * @param firstResult firstResult
     * @param page page to show
     */
    public void loadPersonnes(int firstResult, final int page) {
        view.setSearchLoaderVisible(true);
        final SearchCriteriaModel searchCriterias = new SearchCriteriaModel();
        if (view.getSearch() != null) {
            searchCriterias.setSearchWord(view.getSearch().getValue());
        }
        final RemotePagingCriteriasModel<SearchCriteriaModel> criterias =
            new RemotePagingCriteriasModel<SearchCriteriaModel>(searchCriterias, firstResult, view.getRemotePagingTable().getPageSize());
        criterias.setListeSorts(view.getRemotePagingTable().getSorts());
        if (isTypePersonnel()) {
            loadPersonnel(criterias, page);
        } else {
            loadEleve(criterias, page);
        }
    }

    private void deletePersonne(final Long id) {
        final String msgConfirm = messages.deletePersonneDebMessage() + (isTypePersonnel()
                ? messages.typePersonnel().toLowerCase() : messages.typeEleve().toLowerCase()) + messages.deletePersonneFinMessage();
        fireEventGlobalBus(new ConfirmEvent(msgConfirm, new ConfirmCallback() {
            @Override
            public void onConfirm(boolean isConfirmed) {
                if (isConfirmed) {
                    fireEventGlobalBus(new LoadingEvent(true));
                    if (isTypePersonnel()) {
                        deletePersonnel(id);
                    } else {
                        deleteEleve(id);
                    }
                } else {
                    loadPersonnes();
                }
            }
        }));
    }

    private void loadEleve(RemotePagingCriteriasModel<SearchCriteriaModel> criterias, final int page) {
        eleveServices.getListElevesByCriterias(criterias, new AsyncCallback<RemotePagingResultsModel<EleveModel>>() {
            @Override
            public void onSuccess(RemotePagingResultsModel<EleveModel> result) {
                view.getRemotePagingTable().setRowData(mapListEleveToPersonne(result.getListResults()), page, result.getTotalResults());
                view.setSearchLoaderVisible(false);
            }

            @Override
            public void onFailure(Throwable caught) {
                fireEventGlobalBus(new LoadingEvent(false));
                fireEventGlobalBus(new ErrorEvent(caught));
            }
        });
    }

    private void deleteEleve(final Long id) {
        eleveServices.deleteEleveById(id, new AsyncCallback<Void>() {
            @Override
            public void onFailure(Throwable caught) {
                fireEventGlobalBus(new LoadingEvent(false, LoadingType.ALL));
                fireEventGlobalBus(new ErrorEvent(caught));
            }

            @Override
            public void onSuccess(Void result) {
                loadPersonnes();
                fireEventGlobalBus(new LoadingEvent(false));
            }
        });
    }

    private void loadPersonnel(RemotePagingCriteriasModel<SearchCriteriaModel> criterias, final int page) {
        personnelServices.getListPersonnelsByCriterias(criterias, new AsyncCallback<RemotePagingResultsModel<PersonnelModel>>() {
            @Override
            public void onSuccess(RemotePagingResultsModel<PersonnelModel> result) {
                view.getRemotePagingTable().setRowData(mapListPersonnelToPersonne(result.getListResults()), page, result.getTotalResults());
                view.setSearchLoaderVisible(false);
            }

            @Override
            public void onFailure(Throwable caught) {
                fireEventGlobalBus(new LoadingEvent(false));
                fireEventGlobalBus(new ErrorEvent(caught));
            }
        });
    }

    private void deletePersonnel(final Long id) {
        personnelServices.deletePersonnelById(id, new AsyncCallback<Void>() {
            @Override
            public void onFailure(Throwable caught) {
                fireEventGlobalBus(new LoadingEvent(false, LoadingType.ALL));
                fireEventGlobalBus(new ErrorEvent(caught));
            }

            @Override
            public void onSuccess(Void result) {
                loadPersonnes();
                fireEventGlobalBus(new LoadingEvent(false));
            }
        });
    }

    private List<CellPersonneModel> mapListEleveToPersonne(List<EleveModel> eleves) {
        final List<CellPersonneModel> mappedList = new ArrayList<CellPersonneModel>();
        for (EleveModel eleve : eleves) {
            final CellPersonneModel mappedContact = new CellPersonneModel();
            mappedContact.setId(eleve.getId());
            mappedContact.setNumber(eleve.getNumber());
            mappedContact.setCivility(eleve.getCivility());
            mappedContact.setFirstName(eleve.getFirstName());
            mappedContact.setLastName(eleve.getLastName());
            mappedList.add(mappedContact);
        }
        return mappedList;
    }

    private List<CellPersonneModel> mapListPersonnelToPersonne(List<PersonnelModel> personnels) {
        final List<CellPersonneModel> mappedList = new ArrayList<CellPersonneModel>();
        for (PersonnelModel personnel : personnels) {
            final CellPersonneModel mappedContact = new CellPersonneModel();
            mappedContact.setId(personnel.getId());
            mappedContact.setNumber(personnel.getNumber());
            mappedContact.setCivility(personnel.getCivility());
            mappedContact.setFirstName(personnel.getFirstName());
            mappedContact.setLastName(personnel.getLastName());
            mappedList.add(mappedContact);
        }
        return mappedList;
    }

    private String getNomPrenomsPersonne(CellPersonneModel object) {
        String nomPrenoms = "";
        if (object != null && object.getLastName() != null) {
            nomPrenoms += object.getLastName();
        }
        if (object != null && object.getFirstName() != null) {
            nomPrenoms += " " + object.getFirstName();
        }
        if (nomPrenoms.length() > 10) {
            nomPrenoms = nomPrenoms.substring(0, 9) + "..";
        }
        return nomPrenoms;
    }

    @Override
    public void initView() {
        view.init();
    }

    @Override
    public void onBind() {
        this.addEventHandlerToLocalBus(EditPersonneEvent.TYPE, new EditPersonneEventHandler() {
            @Override
            public void onEditPersonne(EditPersonneEvent event) {
                History.newItem(event.getToken());
            }
        });
        view.getAddEleveBtn().addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                // TODO Auto-generated method stub
                
            }
        });
        view.getAddPersonnelBtn().addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                // TODO Auto-generated method stub
                
            }
        });
        view.getTypeSearchModelSuggestHandler().addValueChangeHandler(new ValueChangeHandler<IdLabelModel>() {
            @Override
            public void onValueChange(ValueChangeEvent<IdLabelModel> event) {
                final boolean personnel = view.getTypeSearchModelSuggestHandler().getValue() != null
                        &&  view.getTypeSearchModelSuggestHandler().getValue().getId() != null
                        && view.getTypeSearchModelSuggestHandler().getValue().getId().equals(ID_TYPE_PERSONNEL);

                view.setSearchLoaderVisible(true);
                final SearchCriteriaModel searchCriterias = new SearchCriteriaModel();
                if (view.getSearch() != null) {
                    searchCriterias.setSearchWord(view.getSearch().getValue());
                }
                final RemotePagingCriteriasModel<SearchCriteriaModel> criterias =
                    new RemotePagingCriteriasModel<SearchCriteriaModel>(searchCriterias, 0, view.getRemotePagingTable().getPageSize());
                criterias.setListeSorts(view.getRemotePagingTable().getSorts());
                if (personnel) {
                    loadPersonnel(criterias, 0);
                } else {
                    loadEleve(criterias, 0);
                }
            }
        });
        view.getTypeSearchModelSuggestHandler().addSuggestHandler(new SuggestListBoxSuggestEventHandler<IdLabelModel>() {
            @Override
            public void suggest(SuggestListBoxSuggestEvent<IdLabelModel> event) {
                fillTypeSearchModel(event);
            }
        });
        view.getSearchHandler().addKeyUpHandler(new KeyUpHandler() {
            @Override
            public void onKeyUp(KeyUpEvent event) {
                loadPersonnes();
            }
        });
        view.getRemotePagingTable().addPageChangeHandler(new PageChangeHandler() {
            @Override
            public void onPageChange(PageChangeEvent event) {
                loadPersonnes(event.getFirstResult(), event.getPage());
            }
        });
        view.getRemotePagingTable().addColumnSortHandler(new ColumnSortHandler() {
            @Override
            public void onColumnSort(ColumnSortEvent event) {
                loadPersonnes();
            }
        });
        view.setButtonsColumnUpdater(new FieldUpdater<CellPersonneModel, String>() {
            @Override
            public void update(int index, CellPersonneModel object, String value) {
                if (actionsUtils.edit().equals(value)) {
                    fireEventLocalBus(new EditPersonneEvent((isTypePersonnel()) ? ID_TYPE_PERSONNEL :  ID_TYPE_ELEVE, object.getId(),
                        getNomPrenomsPersonne(object)));
                } else if (actionsUtils.delete().equals(value)) {
                    deletePersonne(object.getId());
                }
            }
        });
    }

    @Override
    public void onShow(HasWidgets.ForIsWidget container) {
        view.getTypeSearchModelSuggestHandler().setValue(new IdLabelModel(ID_TYPE_ELEVE, messages.typeEleve()));
        container.add(view.asWidget());
    }

    @Override
    public void onDetach() {
    }

    /**
     * Home view interface.
     */
    public interface HomeView extends View {
        /**
         * Get the ClickHandler of the button add personnel.
         * @return the ClickHandler.
         */
        HasClickHandlers getAddPersonnelBtn();

        /**
         * Get the ClickHandler of the button add personnel.
         * @return the ClickHandler.
         */
        HasClickHandlers getAddEleveBtn();

        /**
         * Get the value of search field.
         * @return value
         */
        HasValue<String> getSearch();

        /**
         * Get the handler of search field.
         * @return handler
         */
        HasKeyUpHandlers getSearchHandler();

        /**
         * Show or hide the search loader picture.
         * @param visible true for visible, false for hide
         */
        void setSearchLoaderVisible(boolean visible);

        /**
         * Get handlers for remote paging handlers.
         * @return handlers
         */
        HasRemotePagingTableHandlers<CellPersonneModel> getRemotePagingTable();

        /**
         * Define updater for buttons column.
         * @param updater updater
         */
        void setButtonsColumnUpdater(FieldUpdater<CellPersonneModel, String> updater);

        /**
         * Get handlers for TypeSearchModel.
         * @param handlers
         */
        SuggestListBoxSingle<IdLabelModel> getTypeSearchModelSuggestHandler();
    }

}
